Skip to content

Restore BitArray constructor performance - #131833

Draft
joshuajyue wants to merge 1 commit into
dotnet:mainfrom
joshuajyue:fix-bitarray-constructor-performance
Draft

Restore BitArray constructor performance#131833
joshuajyue wants to merge 1 commit into
dotnet:mainfrom
joshuajyue:fix-bitarray-constructor-performance

Conversation

@joshuajyue

@joshuajyue joshuajyue commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

  • Keep source lengths in normal locals rather than address-exposed out values, removing the Boolean helper's out parameter entirely and delaying byte and integer out assignments until after allocation and copying.
  • Use an int induction variable in the Boolean SIMD and scalar remainder paths so span indexing and bounds analysis stay in the JIT's canonical signed form.

Fixes #131815

Performance

Performance tracking identified regressions in three affected benchmark groups:

  • BitArrayBoolArrayCtor: automated bisection tied the confirmed Size: 512 regression to Add ReadOnlySpan constructors to BitArray #131500 (16.73 ns to 22.65 ns, approximately +35%).
  • BitArrayIntArrayCtor: additional regressions were reported at multiple input sizes and are being validated with a focused Windows x64 EgorBot run.
  • BitArraySetLengthGrow(Size: 4): this benchmark includes construction from byte[]. Automated bisection instead pointed to unrelated JIT commit 0bbeda6cbad4 and flagged possible code-alignment noise, so a focused EgorBot run is validating whether this fix affects it.

A local same-testhost ARM64 paired run of the merged Boolean implementation versus this fix (50 alternating AB/BA pairs) found no statistically significant difference: 37.24 ns versus 37.96 ns, p=0.426. Focused Windows x64 EgorBot runs will validate the original regression environment.

Validation

  • Checked System.Private.CoreLib build
  • 216 BitArray_CtorTests passed with hardware intrinsics enabled
  • 216 BitArray_CtorTests passed with DOTNET_EnableHWIntrinsic=0
  • Read-only review found no correctness, bounds-safety, endianness, or exception-behavior issues

Note

This pull request description was generated with GitHub Copilot and reviewed before publication.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 4236475d-0243-488d-93d8-cd78e94d532b
Copilot AI review requested due to automatic review settings August 4, 2026 22:00
@joshuajyue

Copy link
Copy Markdown
Member Author

@EgorBot -windows_x64 -attempts 3 --filter "Perf_BitArray.BitArrayBoolArrayCtor"

Note

This benchmark request was generated with GitHub Copilot.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tweaks BitArray constructors to reduce JIT impediments in hot paths, with the goal of restoring constructor microbenchmark performance after recent refactoring.

Changes:

  • Avoids using an out-parameter as the source value for allocation sizing in the byte/int span helpers by computing the bit length in a local first.
  • Removes the out bitLength parameter from the Boolean packing helper and assigns _bitLength directly from the source length in the constructors.
  • Switches the Boolean packing loop induction variable to int to keep span indexing and bounds analysis in canonical signed form for the JIT.
Suppressed comments (1)

src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs:342

  • length here is a bit count (values.Length * BitsPerInt32), but the name reads like an element/byte count. Consider renaming it to bitLengthLocal (and updating the two uses) to make the unit explicit.
            int length = values.Length * BitsPerInt32;
            byte[] array = AllocateByteArray(length);

            if (BitConverter.IsLittleEndian)
            {

Comment on lines +175 to +179
int length = bytes.Length * BitsPerByte;
byte[] array = AllocateByteArray(length);

bytes.CopyTo(array);
bitLength = length;
@joshuajyue

Copy link
Copy Markdown
Member Author

@EgorBot -windows_x64 -attempts 3 --filter "Perf_BitArray.BitArrayIntArrayCtor" --filter "Perf_BitArray.BitArraySetLengthGrow"

Note

This benchmark request was generated with GitHub Copilot.

@EgorBo

EgorBo commented Aug 4, 2026

Copy link
Copy Markdown
Member

I've filed an alternative fix to make it fully memory safe #131838 (part of the on-going effort). Let's see if it improves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Perf] Windows/x64: 2 Regressions on 7/31/2026 4:48:31 PM +00:00

3 participants